package com.example.zluserservice.aspect;

import com.example.zluserservice.common.annotation.RedisLock;
import com.example.zluserservice.common.exception.BaseException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

import org.aspectj.lang.reflect.MethodSignature;


/**
 * @Author zhenliu（孙凌岳）
 * @Description Redis分布式锁切面
 * @Date 2025/6/22 10:22
 * @Version 1.0
 */
@Aspect
@Component
public class LockAspect {

    private final RedissonClient redissonClient;
    private final ExpressionParser expressionParser = new SpelExpressionParser();
    private final LocalVariableTableParameterNameDiscoverer parameterNameDiscoverer =
            new LocalVariableTableParameterNameDiscoverer();

    public LockAspect(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @Around("@annotation(lock)")
    public Object handleLock(ProceedingJoinPoint pjp, RedisLock lock) throws Throwable {
        //解析锁key
        String lockKey = parseLockKey(pjp, lock);

        //获取锁对象
        RLock rLock = redissonClient.getLock(lockKey);

        //尝试获取锁
        boolean success = tryAcquireLock(rLock, lock);

        //锁获取失败处理
        if (!success) {
            throw new BaseException("获取分布式锁失败");
        }

        try {
            //执行业务方法
            return pjp.proceed();
        } finally {
            //释放锁
            releaseLockSafely(rLock);
        }
    }

    /**
     * 解析锁key（支持SpEL表达式）
     * @param pjp
     * @param lock
     * @return
     */
    private String parseLockKey(ProceedingJoinPoint pjp, RedisLock lock) {
        //获取方法签名
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        Object[] args = pjp.getArgs();

        //创建SpEL上下文
        StandardEvaluationContext context = new StandardEvaluationContext();

        //获取参数名并设置到上下文
        String[] paramNames = parameterNameDiscoverer.getParameterNames(method);
        if (paramNames != null) {
            for (int i = 0; i < paramNames.length; i++) {
                context.setVariable(paramNames[i], args[i]);
            }
        }

        //解析表达式
        Expression expression = expressionParser.parseExpression(lock.key());
        return expression.getValue(context, String.class);
    }

    /**
     * 尝试获取锁
     * @param rLock
     * @param lock
     * @return
     * @throws InterruptedException
     */
    private boolean tryAcquireLock(RLock rLock, RedisLock lock)
            throws InterruptedException {

        long waitTime = lock.waitTime();
        long leaseTime = lock.leaseTime();
        TimeUnit unit = lock.unit();

        if (lock.block()) {
            //阻塞模式：等待指定时间
            return rLock.tryLock(waitTime, leaseTime, unit);
        } else {
            //非阻塞模式：只尝试一次
            if (leaseTime > 0) {
                return rLock.tryLock(0, leaseTime, unit);
            } else {
                // 使用看门狗自动续期
                return rLock.tryLock();
            }
        }
    }

    /**
     * 尝试获取锁
     * @param rLock
     */
    private void releaseLockSafely(RLock rLock) {
        try {
            if (rLock != null && rLock.isHeldByCurrentThread()) {
                rLock.unlock();
            }
        } catch (IllegalMonitorStateException e) {
            //锁已自动释放
        }
    }
}